package com.zzl.study.demo.tool;

import com.zzl.study.demo.domain.*;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import static com.zzl.study.demo.utils.WeatherFormatUtil.*;

/**
 * 天气API服务
 *
 * @author: zhangzl
 * @date: 2025/10/9 15:49
 * @version: 1.0
 */
public class OpenMeteoService {

    /**
     * Open Meteo免费天气API基础URL
     */
    private static final String OPEN_METEO_BASE_URL = "https://api.open-meteo.com/v1";

    /**
     * 百度地图API基础URL
     */
    private static final String BAIDU_MAP_BASE_URL = "https://api.map.baidu.com/place/v3";

    /**
     * 百度地图API密钥
     */
    @Value("${baidu-map.api-key}")
    private String baiduMapApiKey;

    /**
     * 创建一个RestClient实例，用于发送HTTP请求Open Meteo
     */
    private final RestClient openMeteoRestClient;

    /**
     * 创建一个RestClient实例，用于发送HTTP请求百度地图
     */
    private final RestClient baiduMapRestClient;

    public OpenMeteoService() {
        this.openMeteoRestClient = RestClient.builder()
                .baseUrl(OPEN_METEO_BASE_URL)
                .defaultHeader("Accept", "application/json")
                .defaultHeader("User-Agent", "OpenMeteoClient/1.0")
                .build();
        this.baiduMapRestClient = RestClient.builder()
                .baseUrl(BAIDU_MAP_BASE_URL)
                .build();
    }

    /**
     * 获取指定经纬度的天气预报
     *
     * @param latitude  纬度
     * @param longitude 经度
     * @return 指定位置的天气预报
     * @throws RestClientException 如果请求失败
     */
    @Tool(name = "get_weather_forecast_by_location", description = "获取指定经纬度的天气预报")
    public String getWeatherForecastByLocation(@ToolParam(description = "纬度") double latitude, @ToolParam(description = "经度") double longitude) {
        // 获取天气数据（当前和未来7天）
        WeatherData weatherData = this.openMeteoRestClient.get()
                .uri("/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,apparent_temperature,relative_humidity_2m,precipitation,weather_code,wind_speed_10m,wind_direction_10m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,weather_code,wind_speed_10m_max,wind_direction_10m_dominant&timezone=auto&forecast_days=7",
                        latitude, longitude)
                .retrieve()
                .body(WeatherData.class);

        // 拼接天气信息
        StringBuilder weatherInfo = new StringBuilder();

        // 添加当前天气信息
        CurrentWeather current = weatherData.current();
        String temperatureUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().temperatureUnit() : "°C";
        String windSpeedUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().windSpeedUnit() : "km/h";
        String humidityUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().humidityUnit() : "%";

        weatherInfo.append(String.format("""
                        当前天气:
                        温度: %.1f%s (体感温度: %.1f%s)
                        天气: %s
                        风向: %s (%.1f %s)
                        湿度: %d%s
                        降水量: %.1f 毫米
                        
                        """,
                current.temperature(),
                temperatureUnit,
                current.feelsLike(),
                temperatureUnit,
                getWeatherDescription(current.weatherCode()),
                getWindDirection(current.windDirection()),
                current.windSpeed(),
                windSpeedUnit,
                current.humidity(),
                humidityUnit,
                current.precipitation()));

        // 添加未来天气预报
        weatherInfo.append("未来天气预报:\n");
        DailyForecast daily = weatherData.daily();

        for (int i = 0; i < daily.time().size(); i++) {
            String date = daily.time().get(i);
            double tempMin = daily.tempMin().get(i);
            double tempMax = daily.tempMax().get(i);
            int weatherCode = daily.weatherCode().get(i);
            double windSpeed = daily.windSpeedMax().get(i);
            int windDir = daily.windDirection().get(i);
            double precip = daily.precipitationSum().get(i);

            // 格式化日期
            LocalDate localDate = LocalDate.parse(date);
            String formattedDate = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd (EEE)"));

            weatherInfo.append(String.format("""
                            %s:
                            温度: %.1f%s ~ %.1f%s
                            天气: %s
                            风向: %s (%.1f %s)
                            降水量: %.1f 毫米
                            
                            """,
                    formattedDate,
                    tempMin, temperatureUnit,
                    tempMax, temperatureUnit,
                    getWeatherDescription(weatherCode),
                    getWindDirection(windDir),
                    windSpeed, windSpeedUnit,
                    precip));
        }

        return weatherInfo.toString();
    }

    /**
     * 根据地点名称获取天气预报
     *
     * @param locationName 地点名称
     * @return 天气预报
     * @throws RestClientException 如果请求失败
     */
    @Tool(name = "get_weather_forecast_by_location_name", description = "根据地点名称获取天气预报")
    public String getWeatherForecastByLocationName(@ToolParam(description = "地点名称") String locationName) {
        // 先通过地理编码服务将地点名称转换为经纬度
        BaiduMapApiResponse response = this.baiduMapRestClient.get()
                .uri("/region?query={query}&region={region}&ak={ak}", locationName, locationName, this.baiduMapApiKey)
                .retrieve()
                .body(BaiduMapApiResponse.class);
        BaiduMapRegionLocation baiduMapRegionLocation = response.results().get(0);
        Location location = baiduMapRegionLocation.location();
        // 再调用现有的 getWeatherForecastByLocation 方法
        return getWeatherForecastByLocation(location.latitude(), location.longitude());
    }

    /**
     * 获取指定位置的空气质量信息 (使用备用模拟数据)
     * 注意：由于OpenMeteo的空气质量API可能需要额外配置或不可用，这里提供备用数据
     *
     * @param latitude  纬度
     * @param longitude 经度
     * @return 空气质量信息
     */
    @Tool(name = "get_air_quality", description = "获取指定位置的空气质量信息（模拟数据）")
    public String getAirQuality(@ToolParam(description = "纬度") double latitude, @ToolParam(description = "经度") double longitude) {
        try {
            // 从天气数据中获取基本信息
            WeatherData weatherData = this.openMeteoRestClient.get()
                    .uri("/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m&timezone=auto",
                            latitude, longitude)
                    .retrieve()
                    .body(WeatherData.class);

            // 模拟空气质量数据 - 实际情况下应该从真实API获取
            // 根据经纬度生成一些随机但相对合理的数据
            int europeanAqi = (int) (Math.random() * 100) + 1;
            int usAqi = (int) (europeanAqi * 1.5);
            double pm10 = Math.random() * 50 + 5;
            double pm25 = Math.random() * 25 + 2;
            double co = Math.random() * 500 + 100;
            double no2 = Math.random() * 40 + 5;
            double so2 = Math.random() * 20 + 1;
            double o3 = Math.random() * 80 + 20;

            // 根据AQI评估空气质量等级
            String europeanAqiLevel = getAqiLevel(europeanAqi);
            String usAqiLevel = getUsAqiLevel(usAqi);

            return String.format("""
                            空气质量信息（模拟数据）:
                            
                            位置: 纬度 %.4f, 经度 %.4f
                            欧洲空气质量指数: %d (%s)
                            美国空气质量指数: %d (%s)
                            PM10: %.1f μg/m³
                            PM2.5: %.1f μg/m³
                            一氧化碳(CO): %.1f μg/m³
                            二氧化氮(NO2): %.1f μg/m³
                            二氧化硫(SO2): %.1f μg/m³
                            臭氧(O3): %.1f μg/m³
                            
                            数据更新时间: %s
                            
                            注意: 由于OpenMeteo空气质量API限制，此处显示模拟数据，仅供参考。
                            """,
                    latitude, longitude,
                    europeanAqi, europeanAqiLevel,
                    usAqi, usAqiLevel,
                    pm10,
                    pm25,
                    co,
                    no2,
                    so2,
                    o3,
                    weatherData.current().time());
        } catch (Exception e) {
            // 如果获取基本天气数据失败，返回完全模拟的数据
            return String.format("""
                            空气质量信息（完全模拟数据）:
                            
                            位置: 纬度 %.4f, 经度 %.4f
                            欧洲空气质量指数: %d (%s)
                            美国空气质量指数: %d (%s)
                            PM10: %.1f μg/m³
                            PM2.5: %.1f μg/m³
                            一氧化碳(CO): %.1f μg/m³
                            二氧化氮(NO2): %.1f μg/m³
                            二氧化硫(SO2): %.1f μg/m³
                            臭氧(O3): %.1f μg/m³
                            
                            注意: 由于API限制，此处显示完全模拟数据，仅供参考。
                            """,
                    latitude, longitude,
                    50, getAqiLevel(50),
                    75, getUsAqiLevel(75),
                    25.0,
                    15.0,
                    300.0,
                    20.0,
                    5.0,
                    40.0);
        }
    }

    /**
     * 根据地点名称获取空气质量信息
     *
     * @param locationName 地点名称
     * @return 空气质量信息
     * @throws RestClientException 如果请求失败
     */
    @Tool(name = "get_air_quality_by_location_name", description = "根据地点名称获取空气质量信息（模拟数据）")
    public String getAirQualityByLocationName(@ToolParam(description = "地点名称") String locationName) {
        // 先通过地理编码服务将地点名称转换为经纬度
        BaiduMapApiResponse response = this.baiduMapRestClient.get()
                .uri("/region?query={query}&region={region}&ak={ak}", locationName, locationName, this.baiduMapApiKey)
                .retrieve()
                .body(BaiduMapApiResponse.class);
        BaiduMapRegionLocation baiduMapRegionLocation = response.results().get(0);
        Location location = baiduMapRegionLocation.location();
        return getAirQuality(location.latitude(), location.longitude());
    }

}
